package nostalgia.lastfm;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;

import java.util.*;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import javax.xml.bind.JAXBContext;

import nostalgia.lastfm.model.Weeklychartlist;
import nostalgia.lastfm.model.Weeklyartistchart;
import nostalgia.lastfm.model.Lfm;
import nostalgia.lastfm.model.Weeklytrackchart;
import nostalgia.SystemConfiguration;

public class LastfmQuery {
    private static long RATE_LIMIT = 0;
    private static int HTTP_CLIENT_CONNECTION_TIMEOUT = 2 * 60 * 1000;
    private static int HTTP_CLIENT_SO_TIMEOUT = 2 * 60 * 1000;

    private static Date lastQuery = new Date(0);

    public static long getWeeklyStartToken(int weekago) {
        Calendar date = Calendar.getInstance();
        date.add(Calendar.DAY_OF_YEAR, -14 * weekago);
        date.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
        date.set(Calendar.HOUR, 13);
        date.set(Calendar.MINUTE, 0);
        date.set(Calendar.SECOND, 0);
        date.set(Calendar.MILLISECOND, 0);
        return date.getTime().getTime() / 1000;
    }
    public static Weeklychartlist queryUserWeeklychartlist(String user) throws LastfmQueryException {
        return queryWeeklychartlist("user", user);
    }

    public static Weeklychartlist queryWeeklychartlist(String type, String who) throws LastfmQueryException {
        return query(type + ".getWeeklyChartList",
                new String[] {type, who})
                .getWeeklychartlist().get(0);
    }

    public static Weeklytrackchart queryUserWeeklytrackchart(String user, long from, long to)  throws LastfmQueryException {
        return queryWeeklytrackchart("user", user, from, to); 
    }

    public static Weeklytrackchart queryWeeklytrackchart(String type, String who, long from, long to)  throws LastfmQueryException {
        return query(type + ".getWeeklyTrackChart",
                new String[] {type, who, "from", Long.toString(from), "to", Long.toString(to)})
                .getWeeklytrackchart().get(0);
    }

    /*
    public static Weeklyartistchart queryWeeklyartistchart(String type, String who, long from, long to)  throws LastfmQueryException {
        return query(type + ".getWeeklyArtistChart",
                new String[] {type, who, "from", Long.toString(from), "to", Long.toString(to)})
                .getWeeklyartistchart().get(0);
    }
    */

    protected static Lfm query(String method, String[] params) throws LastfmQueryException {
        try {
            String url = "http://ws.audioscrobbler.com/2.0/?method=" + method;
            for(int i = 0; i < params.length; i++) {
                url += "&" + params[i] + "=" + URLEncoder.encode(params[++i], "UTF-8");
            }
            url += "&api_key=" + SystemConfiguration.getInstsance().getLastfmApiKey();
            return query(url);
        }
        catch(UnsupportedEncodingException e) {
            throw new LastfmQueryException(e);
        }
    }

    protected static synchronized Lfm query(String url) throws LastfmQueryException {
        try {
            long wait = RATE_LIMIT - (new Date().getTime() - lastQuery.getTime());
            if(wait > 0) {
                Thread.sleep(wait);
            }

            //Logger.getLogger(LastfmQuery.class).info("query\t" + url);

            HttpMethod method = new GetMethod(url);
            int result = getHttpClient().executeMethod(method);
            if(result == HttpStatus.SC_NOT_FOUND || result == HttpStatus.SC_BAD_REQUEST) {
                // STRANGE to have SC_BAD_REQUEST for not found users 
                throw new LastfmNotFoundException();
            }
            else if(result != HttpStatus.SC_OK) {
                throw new LastfmQueryException("result " + result);
            }
            Lfm fctval = (Lfm)JaxbFactory.getContext().createUnmarshaller().unmarshal(method.getResponseBodyAsStream());
            if(!fctval.getStatus().equals("ok")) {
                if(fctval.getError().getCode() == 6) {
                    throw new LastfmNotFoundException();
                }
                else {
                    throw new LastfmQueryException(
                            fctval.getError().getCode() + fctval.getError().getValue());
                }
            }
            return fctval;
        }
        catch(LastfmQueryException e) {
            throw e;
        }
        catch(Exception e) {
            throw new LastfmQueryException(e);
        }
        finally {
            lastQuery = new Date();
        }
    }

    protected static HttpClient getHttpClient() {
        HttpClient httpClient = new HttpClient();
        httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(HTTP_CLIENT_CONNECTION_TIMEOUT);
        httpClient.getHttpConnectionManager().getParams().setSoTimeout(HTTP_CLIENT_SO_TIMEOUT);
        return httpClient;
    }

    public static void main(String[] argv) throws LastfmQueryException {
        Weeklychartlist bla = queryWeeklychartlist("user", "slkemfksfsefde");
        bla.toString();
    }
}
